Skip to content

[refactor] Single source of truth for trace span types - #5465

Merged
ardaerzin merged 16 commits into
release/v0.106.1from
ts-chore/unify-span-types
Jul 29, 2026
Merged

[refactor] Single source of truth for trace span types#5465
ardaerzin merged 16 commits into
release/v0.106.1from
ts-chore/unify-span-types

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

Context

A trace span was described by two type definitions: hand-written interfaces and TypeScript enums in oss/src/services/tracing/types, and the zod schemas in @agenta/entities/trace that actually validate the backend payload. They were structurally equivalent but nominally incompatible, so every place data crossed between them needed a cast. PR #5464 added seven such casts with // same backend span shape comments. They were safe, but they meant a real backend shape change could pass through untyped.

They had also already drifted. AGE-3788 canonicalised the backend catch-all category from "undefined" to "unknown", and the entities schema followed. The OSS enum did not.

Changes

@agenta/entities/trace is now the single source. The OSS module keeps only what the package cannot own: TraceSpanNode (the entities node plus the annotations the drawer stores attach at runtime, whose AnnotationDto lives in the app layer) and GenerationDashboardData. Consumers import span types from the package directly rather than through a forwarding module, which is what the app-layer no-re-export lint rule asks for.

The 25 enum value usages became string literals. StatusCode's values were already byte-identical between the two definitions, and SpanCategory differed only in that catch-all. All seven boundary casts are gone.

The drift was a live crash

spanTypeStyles was keyed "undefined" and both consumers destructure the lookup:

const {icon: Icon} = spanTypeStyles[type ?? "undefined"]

A span with span_type: "unknown" misses the record, and destructuring undefined throws. That path reaches the observability table (NodeNameCell) and the trace tree (AvatarTreeContent). The record is now keyed by SpanCategory, so it is exhaustive and a new backend category fails the build, and both call sites fall back instead of throwing.

Nullability

The entities schemas declare | null where the backend sends null. Rather than cast that away, the consumers that were under-declaring it were widened: NodeNameCell, StatusRenderer, TimestampCell and its atom, and ScannedExportRow in the ETL pipeline (which only reads those fields).

Tests / notes

  • pnpm --filter @agenta/oss exec tsc --noEmit and pnpm --filter @agenta/ee exec tsc --noEmit: 0 errors each.
  • pnpm turbo run build --filter=@agenta/entities and pnpm turbo run lint --filter=@agenta/oss: clean.
  • Net 224 deletions against 83 insertions; the OSS types module drops from 156 lines to 35.
  • Stacked on [chore] Zero out web tsc errors and fail builds on type regressions #5464, so review that one first. Base is ts-chore/fix-tsc-issues.

What to QA

  • Observability table and trace tree render for a project with traces. Span icons and status colors look unchanged.
  • Open a trace in the trace drawer, then the session drawer. Tree, overview tab, and annotations tab all populate.
  • Add a span to a testset from the drawer. The add-to-testset flow still reads span data.
  • If you can produce a span with no ag.type set (so the backend reports the unknown category), the observability table renders it with the generic icon instead of crashing. That is the bug this fixes.

ardaerzin added 14 commits July 22, 2026 03:13
The playwright suites are owned and type-checked by the tests workspace,
which holds the @playwright/test dependency; tests/manual scripts import
state modules that no longer exist.
- Restore Parameter, CorrectAnswer and _EvaluationScenario to lib/Types.ts
  (removed by cleanup while consumers remained)
- Re-export MetricColumnDefinition from the EvalRunDetails table barrel
- TooltipButtonProps was renamed EnhancedButtonProps; statusMap moved to
  @agenta/entity-ui/variant
- Synthesize full Parameter objects in UseApiContent
- useRunMetricData: type selection as the unwrapped value, not the atom
- Webhook builders: narrow WebhookFormValues union before field access
- filtersAtom: accept updater functions in the write signature
- Organization settings: drop stale react-query v4 useErrorBoundary and
  migrate setQueriesData to the v5 filters shape
- evaluations/utils: surface variantId from invocation metadata
- PreviewTableRow: add index signature required by InfiniteTableRowBase
Mirrors the vetted WP-4e-2a in-place fixes from fe-chore/move-evals-to-packages
(adapted to OSS import paths) plus new fixes for the component layer:

- restore PreviewTestCase to lib/Types; re-export MetricProcessor/MetricScope
- add "input" to EvaluationColumnKind (backend mapping emits it) and type the
  visibility-label column extension in buildPreviewColumns
- recharts v3 API drift in EvaluatorMetricsChart (TooltipContentProps, tuple
  radius, MetricStripEntry contextual typing)
- widen evaluationType to EvaluationRunKind; type query atoms as nullable
- latent runtime bugs typed as-is per WP-4e-2a convention (applyAggregatesToRaw,
  metricProcessor ReferenceErrors, dead metric-group branches) - flagged, not fixed
…ant id

- createPaginatedEntityStore: refreshAtom/actions.refresh accept an optional
  value or updater (bare set() still bumps the counter) - clears the
  'Expected 0 arguments' cluster across testset modals and other consumers
- EnvironmentStatus: variant.id optional; the component already guards it
…ing layers; oss tsc 347->105

Parallel per-area pass, behavior-preserving throughout (latent runtime bugs are
typed as-is with NOTE comments per the WP-4e-2a convention, not silently fixed):

- TraceSpanNode OSS<->entities dual-type: aligned at every crossing with
  documented boundary casts (16 errors -> 0); annotations field added to the
  OSS node (drawer stores attach it at runtime)
- SharedDrawers: broken SessionDrawerButton imports repaired, JSON-schema
  access typed, null-safety in SelectEvaluators/AnnotateDrawer, drawer
  payload/ref/label types aligned with runtime
- observability: extended-column types for custom antd props, TraceRow/
  SessionRow InfiniteTableRowBase conformance, traceTabsAtom updater support,
  ag-attributes selector typing at the producer
- playground/url-state: removed drifted local duplicates of package types,
  eagerAtom->atom where deps are sync, modal/store prop alignment
- onboarding/testsets/org: updater-widened widget UI atom, tour placement
  vocabulary fix, OnboardingLoader next/dynamic compat, org provider API
  types matched to the backend wire shape (settings/flags dicts)
- EvalRunDetails remainder: WP-4e-2a-vetted atom fixes adapted, recharts v3
  formatter/content signatures, stale import paths repointed

Flagged for triage (typed as-is): AddToTestsetDrawer trace draft discard/
update call missing molecule actions; orphaned SessionDrawerButton
…atterns; oss tsc 105->82

- InfiniteVirtualTable: canonical ExtendedColumnType exported from the barrel
  (three local extended-column copies repointed as aliases); pure read fn for
  columnHiddenKeys (write path reconciles versions); memo generic preservation;
  onRow/getRowProps/rowSelection.fixed aligned with antd 6
- antd v6 PopoverStylesType 'body' drops typed as-is (2 sites, existing pattern)
- React 19: JSX.Element -> ReactElement in RequireWorkflowKind
- AgentaNodeDTO: trace_id/span_id added (backend SpanDTO sends them)
- Org.default_workspace: list endpoint strips it - both lookups are latent
  dead code, typed as-is with comments
- FiltersPreview: operatorLabel is a display string, not the operator union
…windowing export

- DrillInUIContext: EditorProvider/SharedEditor slots typed with the real
  component prop types instead of a hand-written subset (root cause of the
  OSS provider assignment errors)
- InfiniteVirtualTable: rowSelection.fixed accepts antd FixedType; locale
  accepted (not yet forwarded - flagged); Editor barrel exports CodeLanguage
- workflow barrel exports WorkflowRevisionWindowing
Wave-2 parallel pass over the final ~105 OSS + 11 EE-only errors, behavior-
preserving throughout (latent bugs typed as-is with NOTE comments per the
WP-4e-2a convention):

- Evaluators/Evaluations: generic evaluator filtering, chart datum typing
- pages/evaluations: NewEvaluation modal props retyped to entities-package
  shapes (stale legacy Types imports dropped); antd 6 tabPlacement vocabulary
- Testcases/Testsets/Deployments: canonical ExtendedColumnType adoption,
  dataset-store variance via Pick<...,'hooks'>
- DrillInView/EditorViews: TMode narrowing via consts, Format|CodeLanguage
  state union, html format menu typing
- app-shell misc: services/state/pages sweep with backend-verified API types
- EE Billing + misc EE-only files

Latent bugs surfaced and typed as-is (chips filed where actionable):
workspace rename sends invalid org PATCH body; SessionInspector reads
status.code the backend never sends; axios.isCancel dead on created
instance; invite accept can interpolate undefined ids
Both apps are at zero tsc errors; flip ignoreBuildErrors to false so
next build guards the baseline. Verified: full oss and ee builds pass
with the gate on.
The OSS tracing module declared its own TraceSpan/TraceSpanNode plus four
TypeScript enums that mirrored the zod schemas in @agenta/entities. The two
were structurally equivalent but nominally incompatible, so every crossing
needed a cast (7 of them, added in #5464 as documented scar tissue).

- oss/src/services/tracing/types now owns only TraceSpanNode (entities node
  plus the annotations the drawer stores attach) and GenerationDashboardData;
  consumers import span types from @agenta/entities/trace directly, per the
  app-layer no-re-export rule
- 25 enum value usages become string literals; StatusCode values were already
  identical, SpanCategory differed only in the catch-all
- all 7 boundary casts removed

Fixes a live crash: AGE-3788 canonicalised the backend catch-all from
"undefined" to "unknown", but spanTypeStyles was still keyed "undefined" and
both consumers destructure the lookup, so any span typed "unknown" threw a
TypeError in the observability table and trace tree. The record is now keyed
by SpanCategory (exhaustive, so a new backend category fails the build) with a
fallback at both call sites.

Nullability now matches the payload: entities declares | null where the
backend sends null, so NodeNameCell/StatusRenderer/TimestampCell props and
ScannedExportRow were widened rather than cast.
@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 29, 2026 10:13am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a964adcc-b639-498d-ad65-2886e072e6ff

📥 Commits

Reviewing files that changed from the base of the PR and between 8b98619 and 5b2b3db.

📒 Files selected for processing (16)
  • web/oss/src/components/EvalRunDetails/atoms/traces.ts
  • web/oss/src/components/SharedDrawers/SessionDrawer/store/sessionDrawerStore.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/OverviewTabItem/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/assets/spanVisibility.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/store/traceDrawerStore.ts
  • web/oss/src/components/pages/observability/assets/constants.ts
  • web/oss/src/components/pages/observability/assets/exportUtils.ts
  • web/oss/src/components/pages/observability/components/AvatarTreeContent.tsx
  • web/oss/src/components/pages/observability/components/NodeNameCell.tsx
  • web/oss/src/components/pages/observability/components/StatusRenderer.tsx
  • web/oss/src/components/pages/observability/components/TimestampCell.tsx
  • web/oss/src/services/tracing/types/index.ts
  • web/oss/src/state/newObservability/atoms/queries.ts
  • web/packages/agenta-entities/src/trace/etl/exportMatchingTraces.ts
  • web/packages/agenta-entities/tests/unit/export-matching-traces.test.ts

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved trace and span handling for missing or unknown categories, statuses, timestamps, and names.
    • Preserved anonymous trace rows during deduplicated exports.
    • Ensured errored spans display consistently across trace views.
  • Tests

    • Added coverage for exporting rows without trace or span identifiers.

Walkthrough

Changes

Tracing type alignment

Layer / File(s) Summary
Shared tracing contracts
web/oss/src/services/tracing/types/index.ts, web/oss/src/components/..., web/oss/src/services/...
OSS tracing types now extend entity definitions, and consumers import shared response and span types as type-only dependencies.
Trace drill-in compatibility
web/oss/src/components/SharedDrawers/TraceDrawer/.../OverviewTabItem/index.tsx
The active trace is passed directly to the drill-in molecule without an explicit type cast.
Observability UI mappings
web/oss/src/components/SharedDrawers/TraceDrawer/..., web/oss/src/components/pages/observability/..., web/oss/src/state/newObservability/...
Category and status mappings use shared types and string literals, with "unknown" and nullable input fallbacks supported.

Trace export deduplication

Layer / File(s) Summary
Export row contract and deduplication
web/packages/agenta-entities/src/trace/etl/exportMatchingTraces.ts, web/packages/agenta-entities/tests/unit/export-matching-traces.test.ts
Export rows derive identifier fields from TraceSpan; rows missing identity receive unique generated deduplication keys and are covered by a new test.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • Agenta-AI/agenta#5464: Overlaps the OSS tracing type boundary and span/response type updates.
  • Agenta-AI/agenta#4404: Overlaps key-span filtering and error status handling in spanVisibility.ts and TraceTree.
  • Agenta-AI/agenta#4573: Overlaps the migration of session and trace drawer code to shared tracing entities.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main refactor to make trace span types come from a single source of truth.
Description check ✅ Passed The description is directly about the trace-span typing refactor and related nullability and crash fixes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ts-chore/unify-span-types

Comment @coderabbitai help to get the list of available commands.

@ardaerzin
ardaerzin requested a review from ashrafchowdury July 23, 2026 19:34
@ardaerzin
ardaerzin marked this pull request as ready for review July 23, 2026 19:34
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. refactoring A code change that neither fixes a bug nor adds a feature labels Jul 23, 2026
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
web/oss/src/services/tracing/types/index.ts (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the source-of-truth comment.

This three-line explanation exceeds the repository rule for in-code comments; keep it to one short line or move the rationale to documentation.

As per coding guidelines, in-code comments must be at most one short line unless they document a genuinely surprising constraint.

Suggested wording
-// Span types have a single source of truth: the zod schemas in `@agenta/entities`,
-// which validate the backend payload at the boundary. Import them from
-// `@agenta/entities/trace` directly; this module owns only the OSS-only additions.
+// Span types come from `@agenta/entities`; this module adds OSS annotations.

Source: Coding guidelines

web/oss/src/components/pages/observability/assets/constants.ts (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial

Verify the required frontend lint-fix command.

Before committing this frontend cohort, run pnpm lint-fix from web; the supplied context does not confirm that exact command. As per coding guidelines, frontend changes must run pnpm lint-fix from web.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d6f21bdb-d182-4f40-b36c-46c0492e89b2

📥 Commits

Reviewing files that changed from the base of the PR and between cb415f6 and 9301719.

📒 Files selected for processing (19)
  • web/oss/src/components/EvalRunDetails/atoms/traces.ts
  • web/oss/src/components/SharedDrawers/SessionDrawer/store/sessionDrawerStore.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/OverviewTabItem/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceContent/components/TraceTypeHeader/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceHeader/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/assets/spanVisibility.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/components/TraceTree/index.tsx
  • web/oss/src/components/SharedDrawers/TraceDrawer/store/openInPlayground.ts
  • web/oss/src/components/SharedDrawers/TraceDrawer/store/traceDrawerStore.ts
  • web/oss/src/components/pages/observability/assets/constants.ts
  • web/oss/src/components/pages/observability/assets/exportUtils.ts
  • web/oss/src/components/pages/observability/components/AvatarTreeContent.tsx
  • web/oss/src/components/pages/observability/components/NodeNameCell.tsx
  • web/oss/src/components/pages/observability/components/StatusRenderer.tsx
  • web/oss/src/components/pages/observability/components/TimestampCell.tsx
  • web/oss/src/services/tracing/types/index.ts
  • web/oss/src/state/newObservability/atoms/queries.ts
  • web/oss/src/state/newObservability/atoms/queryHelpers.ts
  • web/packages/agenta-entities/src/trace/etl/exportMatchingTraces.ts

Comment thread web/packages/agenta-entities/src/trace/etl/exportMatchingTraces.ts Outdated
@ashrafchowdury

Copy link
Copy Markdown
Contributor

Web build is failing

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 25, 2026
@ardaerzin
ardaerzin changed the base branch from ts-chore/fix-tsc-issues to release/v0.106.1 July 29, 2026 09:29
Conflicts were all in the same direction: release/v0.106.1 carries the
pre-refactor span typing (#5464's boundary casts and narrow prop types)
that this branch removes. Resolved to this branch's side in all six files.

- services/tracing/types: keep the entities-backed TraceSpanNode; the
  release-side annotations field is already on it
- OverviewTabItem: drop the EntityTraceSpan cast and its now-unused import
- traceDrawerStore: SpanLink/TracesResponse come from @agenta/entities/trace
- NodeNameCell/TimestampCell/formattedTimestampAtomFamily: keep the widened
  nullable types

oss and ee tsc both at 0 errors after the merge.
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-07-29T10:52:26.802Z

Review follow-up on the export ETL row contract.

ScannedExportRow hand-redeclared five span fields, and this branch widened all
five to `| null` to make TraceSpanNode assignable. Only three needed it: the
schema declares trace_id and span_id required and non-null, so widening those
two moved the contract away from the source of truth in the one file this PR
was meant to unify.

Pick the four span fields from TraceSpan instead. Same assignability for the
caller, but the contract can no longer drift.

That widening was what made the dedup collapse CodeRabbit flagged expressible:
rows missing both ids all hashed to the fallback `trace_id:parent_id:start_time`,
so a page of them deduped down to one row. Not reachable from the tracing API
(zod validates both ids at the boundary), but the fallback was wrong on its own
terms — it also collapsed sibling spans sharing a parent and start_time. Dedup
may only drop what it can prove is a repeat, so a row with no identity is now
passed through undeduped: for an export, a duplicate row beats a dropped one.

Also shortens the span-types comment to the one-line repo rule.

Test added for the no-identity path; it fails on the old key (1 row, not 3).
@ardaerzin

Copy link
Copy Markdown
Contributor Author

Rebased onto release/v0.106.1 and worked through the review items.

Merge conflicts (a1119c3) — six files, all the same shape: release/v0.106.1 carries the pre-refactor span typing that this PR removes (the boundary casts from #5464 and the narrow prop types), so every conflict resolved to this branch's side. Verified per file by diffing HEAD against the release branch rather than reading the conflict blocks alone — in each case the release side was a strict subset of what this branch already does. One thing the conflict markers didn't show: git had auto-merged the release side's type TraceSpan as EntityTraceSpan import into OverviewTabItem outside the conflict region, which would have left an unused import once the cast was dropped.

@ashrafchowdury "web build is failing" — that was before the merge; the branch was stale against the release. All checks pass on the current head, including build / build-image (agenta-web) on both arches.

CodeRabbit's defaultSelectKey finding — real, and fixed at its cause rather than at the key function. Details in the inline thread; short version is that this PR widened ScannedExportRow's trace_id/span_id to | null when the schema declares both required, so the row contract drifted from the source of truth in the one file the PR exists to unify. It now Picks those fields from TraceSpan. Not reachable in production — the only caller passes zod-validated spans.

Nitpicks — shortened the span-types comment to the one-line rule in web/CLAUDE.md, and ran pnpm lint-fix (11/11 clean, reformatted nothing).

Verification on 5b2b3db: entities suite 923 passing (one added for the no-identity path), turbo build --filter=@agenta/entities clean, oss and ee tsc both at 0.

One judgment call worth a second opinion: making trace_id/span_id required narrows an exported package type. It's safe across this repo — one caller plus the test fixture, both already required, and all three type-checks pass — but it would be a breaking change for any consumer of @agenta/entities outside the monorepo. I don't believe there is one; flagging it in case someone knows otherwise.

@ardaerzin
ardaerzin merged commit 7b30f36 into release/v0.106.1 Jul 29, 2026
59 checks passed
ardaerzin added a commit that referenced this pull request Jul 29, 2026
…trial

Resolves conflicts from #5464/#5465 landing upstream: this branch carried
pre-review copies of the same span-type consolidation. Took the merged
versions of services/tracing/types and exportMatchingTraces, and accepted
the upstream deletion of the dead SessionDrawerButton.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer refactoring A code change that neither fixes a bug nor adds a feature size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants